//////////////////////////////////////////////////////////////////////
//
// CryEngine Source code
//
// File:SkeletonAnim.h
// Implementation of Skeleton class
//
// History:
// August 6, 2005: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////

#ifndef _SKELETON_ANIM_H
#define _SKELETON_ANIM_H



#include <SpuUtils.h>
#include "SkeletonPose.h"
#include "AnimationThreadTask.h"
#include "FacialAnimation/FacialModel.h"
#include "Memory/Memory.h"
#include "Memory/Pool.h"
class CSkeletonAnim;
class CSkeletonPose;
class CCharInstance;
struct ModelAnimationHeader;

namespace Command
{
	class CBuffer;
}

struct AnimInfo
{
	int m_nAnimID;
	f32 m_fAnimTime;
	f32 m_fWeight;
	f32 m_fPlaybackScale;
};

class CTransitionQueue
{
public://private:
	DynArray<CAnimation> m_animations;
	f32 m_additiveWeights;
	f32 m_speedMultiplier;
	f32 m_blending;
	f32 m_blendingTime;
	f32 m_blendingMultiplier;
	// manually defines how much the first and the second animation
	// in the transition queue is mixed 
	// 0.0f means only animation [0], 1.0f means only animation [1]
	f32 m_manualMixingWeight;

	CTransitionQueue() { Reset(); };

	void Reset();
	void SetFirstLayer();
	void ApplyManualMixingWeight( uint32 numAnims );
	void SetManualMixingWeight( f32 weight ) { m_manualMixingWeight = weight; };
	void TransitionsBetweenSeveralAnimations( uint32 numAnims );
	void AdjustTransitionWeights( uint32 numAnims );
	void UpdateTransitionTime( uint32 numAnims, f32 fDeltaTime, f32 trackViewExclusive, f32 originalDeltaTime );
	void AdjustAnimationTimeForTimeWarpedAnimations( uint32 numAnims );
};

class CLayer
{
public://private:
	CTransitionQueue m_transitionQueue;
	IAnimationPoseModifierPtr m_poseModifier;
};

#define numVIRTUALLAYERS (0x10)

// queue used for threading to prevent resizing of m_arrFIFO vectors while computations are running on these
struct DeferredQueueUpdate
{
	DeferredQueueUpdate( bool bRemoveFirstAnim, int32 nLayer, const CAnimation &rAnim ) :
		m_bRemoveFirstAnim(bRemoveFirstAnim), m_nLayer(nLayer), m_Anim(rAnim) {}

	void GetMemoryUsage(ICrySizer *pSizer ) const{}

	CAnimation m_Anim;
	int32 m_nLayer;
	bool m_bRemoveFirstAnim;
};

class CSkeletonAnimTask :
	public CAnimationThreadTask
{
public:
	static bool Initialize();

	static Memory::CPoolFrameLocal* GetMemoryPool() { return s_pMemoryPool; }

private:
	static Memory::CPoolFrameLocal* s_pMemoryPool;

public:
	CSkeletonAnimTask();
	~CSkeletonAnimTask();

public:
	void Initialize(CSkeletonAnim& skeletonAnim);

	void Begin(const QuatT& locationPhysics, const QuatTS& locationAnimation, bool immediate);
	void Wait();

	void Prepare();
	void Synchronize();

	bool IsProcessed() { return m_bProcessed; }

	// CAnimationThreadTask
public:
	void Execute();
	void SPU_Execute(int randomSeed);

private:
	CSkeletonAnim* m_pSkeletonAnim;
	QuatT m_locationPhysics;
	QuatTS m_locationAnimation;

	bool m_bProcessed;

private:
	JobManager::SJobState m_jobState;
};

//-------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------
class CSkeletonAnim : public ISkeletonAnim
{
private:
	friend class CSkeletonAnimTask;

public:
	CSkeletonAnim();
	~CSkeletonAnim();

	CFacialDisplaceInfo m_facialDisplaceInfo;


	size_t SizeOfThis ();
	void GetMemoryUsage(ICrySizer * pSizer) const;
	void Serialize(TSerialize ser);

	void SetCharEditMode( uint32 m );

	// no editor on ps3, so no need for code related to editor



	bool GetCharEditMode() { return m_CharEditMode; };



	void SetAnimationDrivenMotion(uint32 ts);
	void SetMirrorAnimation(uint32 ts);
	uint32 GetAnimationDrivenMotion() { return m_AnimationDrivenMotion; };

	void SetTrackViewExclusive(uint32 i);
	void SetTrackViewMixingWeight(uint32 layer, f32 weight);

	uint32 GetTrackViewStatus() {	return m_TrackViewExclusive;	};

	// [*DavidR | 22/Mar/2010] BEGIN: 
	// TEMPORARY METHODS UNTIL m_arrDeferredQueueUpdates GETS REMOVED
	VIRTUAL int  GetNumAnimsInFIFOAndDeferredQueue(uint32 nLayer);
	VIRTUAL CAnimation& GetAnimFromFIFOAndDeferredQueue(uint32 nLayer, uint32 num);
	// [*DavidR | 22/Mar/2010] END 


	//! Set the current time of the given layer, in seconds
	void SetLayerTime (uint32 nLayer, float fTimeSeconds);
	//! Return the current time of the given layer, in seconds
	float GetLayerTime (uint32 nLayer) const;

	// sets the animation speed scale for layers
	void SetLayerUpdateMultiplier(int32 nLayer, f32 fSpeed);
	f32 GetLayerUpdateMultiplier(uint32 nLayer)
	{
		if (nLayer<numVIRTUALLAYERS)
			return m_layers[nLayer].m_transitionQueue.m_speedMultiplier;
		return 1.0f;
	}

	void SetAdditiveWeight(int32 nLayer, f32 fWeight);
	void SetLayerBlendMultiplier(int32 nLayer, f32 fMult);

	ILINE uint8 GetActiveLayer(uint8 layer)
	{
		return (m_ActiveLayer >> layer) & 1;
	}

	ILINE void SetActiveLayer(uint8 layer, uint8 val)
	{
		uint32 mask2 = 1 << layer;
		uint32 mask = (val & 0x1) << layer;
		m_ActiveLayer = (m_ActiveLayer & ~mask2) | mask;
	}


	VIRTUAL void SetDesiredLocalLocation(const QuatT& desiredLocalLocation, float deltaTime, float frameTime, float turnSpeedMultiplier);
	VIRTUAL void SetDesiredMotionParam(EMotionParamID id, float value, float deltaTime, bool initOnly = false);
	VIRTUAL void SetDesiredMotionParam2(EMotionParamID id, float value, float deltaTime, bool initOnly = false);
	VIRTUAL float GetDesiredMotionParam(EMotionParamID id) const;
	VIRTUAL void SetBlendSpaceOverride(EMotionParamID id, float value, bool enable); // Enable/Disable direct override of blendspace weights, used from CharacterEditor.


	void SetDesiredMotionParamsFromDesiredLocalLocation(const CAnimation *arrAFIFO, uint32 numAnimsInQueue, float frameTime);
	void SetClampedMotionParam(SParametric& lmg, EMotionParamID id, float newValue, float deltaTime, bool initOnly);

	void UpdateMotionParamDescs(SParametric& lmg, float transitionBlendWeight);
	void ConvertLMGCapsToMotionParamDesc(EMotionParamID id, MotionParamDesc& desc, const LMGCapabilities& caps);
	void UpdateMotionParamBlendSpacesForActiveMotions(float deltaTime);
	void UpdateMotionBlendSpace(SParametric& lmg, float deltaTime, float transitionBlendWeight);
	void UpdateMotionParamBlendSpace(SParametric& lmg, EMotionParamID id); // Update blendspace for the given parameter
	void CalculateMotionParamBlendSpace(MotionParam& param, float undefined); // Helper function to UpdateMotionParamBlendSpace

	// Converts new blendspace into old blendspace (and updates/interpolates strafe vector)
	void UpdateOldMotionBlendSpace(SParametric& lmg, float frameTime, float transitionBlendWeight);

	// Converts new blendspace into old blendspace (and updates/interpolates strafe vector)
	void UpdateOldMotionBlendSpace(SParametric& lmg, float frameTime);

	void ProcessAnimations(  const QuatT& rPhysLocationCurr, const QuatTS& rAnimCharLocationCurr, uint32 OnRender, uint32 UpdateQueue, bool immediate = false);
	uint32 BlendManager (f32 deltatime, DynArray<CAnimation>& arrLayer, uint32 nLayer );
	uint32 BlendManagerDebug ( DynArray<CAnimation>& arrLayer, uint32 nLayer );
	void LayerBlendManager( f32 fDeltaTime, uint32 nLayer );
	void UpdateAndPushIntoExecBuffer( CAnimation *arrAFIFO, uint32 nLayer, uint32 NumAnimsInQueue, uint32 AnimNo );

	Vec3 m_AccVelocity;
	QuatT m_desiredLocalLocation;
	float m_desiredArrivalDeltaTime;
	float m_desiredTurnSpeedMultiplier; // Makes players turn/catchup faster, being more responsive to quick turns.
	f32 m_fDesiredTurnSpeedOffset;
	f32 m_fDesiredTurnSpeedScale;
	f32 m_fDesiredTurnSpeedBlend;
	// Enable/Disable direct override of blendspace weight values, primarily used from CharacterEditor.
	float m_desiredMotionParam[eMotionParamID_COUNT];
	bool m_CharEditBlendSpaceOverrideEnabled[eMotionParamID_COUNT];
	float m_CharEditBlendSpaceOverride[eMotionParamID_COUNT];

	DynArray<DeferredQueueUpdate> m_arrDeferredQueueUpdates;
	CLayer m_layers[numVIRTUALLAYERS];
	int32 m_arrStopAnimationQueue[numVIRTUALLAYERS]; // used on spu as a queue to dereff stopAnimationCalls

	int (*m_pEventCallback)(ICharacterInstance*,void*);
	void* m_pEventCallbackData;
	void SetEventCallback(CallBackFuncType func, void *pdata)
	{
		m_pEventCallback = func;
		m_pEventCallbackData = pdata;
	}
	AnimEventInstance GetLastAnimEvent() { return m_LastAnimEvent; };

	void AnimCallback ( CAnimation *arrAFIFO, uint32 AnimNo, uint32 AnimQueueLen);


	AnimEventInstance m_LastAnimEvent;

	uint32 m_IsAnimPlaying;

	uint8 m_AnimationDrivenMotion;
	uint8 m_ShowDebugText;
	uint16 m_MirrorAnimation;
	uint16 m_ActiveLayer;

	void SetDebugging( uint32 debugFlags );

	bool m_CharEditMode: 1; //this is a flag register
	bool m_TrackViewExclusive : 1;
	uint32 m_bReinitializeAnimGraph : 1;
	
	Vec3 GetCurrentVelocity() {	return m_AccVelocity; };
	f32 GetCurrentSlope() 
	{ 
		const QuatT& relmove=GetRelMovement();
		f32 fMotionDirection = Ang3::CreateRadZ(Vec3(0,1,0),relmove.t);
		Vec3 v = relmove.t*Matrix33::CreateRotationZ(fMotionDirection);
		f32 fMotionSlope     = atan2_tpl(v.z,v.y);
		return fMotionSlope; 
	};
	const QuatT& GetRelMovement() const;
	f32 GetRelRotationZ(f32 assetRot);
	f32 GetUserData(int i){ return m_fUserData[i]; }
	void InitSkeletonAnim( CCharInstance* pInstance, CSkeletonPose* pSkeletonPose );
	void ProcessAnimationUpdate(const QuatT rPhysLocationCurr, const QuatTS rAnimLocationCurr, uint32 UpdateQueue);
	void SyncProcessAnimations();

	void FinishAnimationComputations();
	void SyncData();

	void SetPostProcessParameter( const QuatT &rPhysLocationNext, const QuatTS &rAnimLocationNext, IAttachment*	pIAttachment, float fZoomAdjustedDistanceFromCamera, uint32 nOnRender );

	f32	m_fAllowMultilayerAnim;
	f32 m_fUserData[NUM_ANIMATION_USER_DATA_SLOTS];

	CSkeletonPose* m_pSkeletonPose;
	CCharInstance* m_pInstance;


	bool m_bDidRun;
	bool m_bNeedPostProcess;
	bool m_bSetDefaultPose;
	bool m_bTimeUpdated;

	QuatT m_PostProcessPhysLocationNext;
	QuatTS m_PostProcessAnimLocationNext;
	IAttachment* m_pPostProcessIAttachment;
	float m_fPostProcessZoomAdjustedDistanceFromCamera;
	uint32 m_nPostProcessOnRender;

	uint32 nStopAnimationQueuePos;

	AnimEventInstance spuEventQueue[5];
	uint32 m_nEventQueuePos;
	QuatT m_RelativeMovement;

	bool m_hasInvalidController;
	const char *m_pWrongControllerName;

	CSkeletonAnimTask m_threadTask;

	f32 m_fDesiredMoveSpeedSmooth;
	f32 m_fDesiredMoveSpeedSmoothRate;
	f32 m_fDesiredTurnSpeedSmooth;
	f32 m_fDesiredTurnSpeedSmoothRate;
	Vec2 m_fDesiredStrafeSmooth;
	Vec2 m_fDesiredStrafeSmoothRate;

	DynArray<IAnimationPoseModifierPtr> m_poseModifiers;
	DynArray<IAnimationPoseModifierPtr> m_poseModifiersPostExecute;

	void PushLayer(IAnimationPoseModifierPtr poseModifier);

	void SetPoseModifier(uint32 layer, IAnimationPoseModifierPtr poseModifier) { m_layers[layer].m_poseModifier = poseModifier; }
	IAnimationPoseModifier* GetPoseModifier(uint32 layer) { return m_layers[layer].m_poseModifier.get(); }

	void PoseModifiersPrepare(const QuatT& locationPhysics, const QuatTS& locationAnimation);
	void PoseModifiersExecutePost(Skeleton::CPoseData& poseData, const QuatT& locationPhysics, const QuatTS& locationAnimation);
	void PoseModifiersSynchronize();

	DynArray<uint16> m_arrJointMask; //limited to 16 layers

	uint8 GetJointMask(int32 nJointNo, uint8 layer);
	void SetJointMask(int32 nJointNo, uint8 layer, uint8 val);
	//enable & disable animation for one specified bone and layer
	bool SetJointMask(const char* szBoneName, uint32 nLayerNo, uint8 nVal);
	//enable & disable animation for one full layer
	bool SetLayerMask(uint32 nLayerNo, uint8 nVal);

	// Transition Queue
private:
	uint32 AnimationToQueue( const ModelAnimationHeader* pAnim0,int a0, const ModelAnimationHeader* pAnimAim0,int aid0,const ModelAnimationHeader* pAnimAim1,int aid1, f32 btime, const CryCharAnimationParams& AnimParams);
	void UnloadAnimationAssets(DynArray<CAnimation>& arrAFIFO, int num);

	// Interface
public:
	bool StartAnimation(const char* szAnimName0, const CryCharAnimationParams& Params);
	bool StartAnimationById(int32 id, const CryCharAnimationParams& Params);
	bool StopAnimationInLayer(int32 nLayer, f32 BlendOutTime);
	bool StopAnimationsAllLayers();

	void ClearFIFOLayer(uint32 nLayer);

	int GetNumAnimsInFIFO(uint32 nLayer) const;
	bool RemoveAnimFromFIFO(uint32 nLayer, uint32 num, bool forceRemove=false);
	CAnimation& GetAnimFromFIFO(uint32 nLayer, uint32 num);
	CAnimation* FindAnimInFIFO( uint32 nUserToken, int nLayer=1, bool includeDeferredAnimation = false );
	void ManualSeekAnimationInFIFO(uint32 nLayer, uint32 num, float time, bool triggerAnimEvents);
	void Commands_CreateAimPoseQueue(SDirectionalBlender& rDirK, uint32 useDirPosesInBaseAnim);

	// makes sure there's no anim in this layer's queue that could cause a delay (useful when you want to play an
	// animation that you want to be 100% sure is going to be transitioned to immediately)
	VIRTUAL void RemoveTransitionDelayConditions(uint32 nLayer);

private:
	void AppendAnimationToQueue( int32 nLayer, const CAnimation &rAnim );

	uint32 EvaluateTransitionFlags( CAnimation *arrAnimFiFo, uint32 numAnims );

	uint32 IsAnimationInMemory( CAnimationSet* pAnimationSet, CAnimation* pAnimation );
	void UpdateParameters( CAnimation *arrAnimFiFo, uint32 nMaxActiveInQueue, uint32 nLayer, f32 fDeltaTime );
	void LMGBlendWeightCalculation( CAnimation *arrAnimFiFo, uint32 numAnims );
	void ComputeBlendWeights( CAnimation *arrAnimFiFo, uint32 numAnims, f32 fDeltaTime, uint32 nLayer );
	void UpdateAnimTimeAndExecBuffer( CAnimation *arrAnimFiFo, uint32 numAnims, uint32 nLayer, uint32 NumAnimsInQueue );

private:
	void Commands_Create(const QuatT& rPhysLocationCurr, const QuatTS& rAnimLocationCurr, Command::CBuffer& buffer);
	void Commands_BasePlayback(const CAnimation& rAnim, Command::CBuffer& buffer);
	void Commands_BaseEvaluationLMG(const CAnimation& rAnim, uint32 nTargetBuffer, Command::CBuffer& buffer);
	void Commands_LPlayback(const CAnimation& rAnim, uint32 nTargetBuffer, uint32 nVLayer, uint32 mask, Command::CBuffer& buffer);


	void ParseLocator(uint32 numAnimsInLayer);
	void ParseLayer0(const CAnimation& rAnim, AnimInfo* ainfo,uint32& acounter);
	void Locator_Evaluation(AnimInfo* pAInfo,uint32& acounter,struct SLocator*	pLocatorDst);
	IController* GetRootController(GlobalAnimationHeaderCAF& rCAF);

} _ALIGN(128);

#endif // _SKELETON_ANIM_H
